package com.gitee.qdbp.base.controlling;

import java.lang.reflect.Method;
import java.util.List;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.web.multipart.MultipartFile;
import com.gitee.qdbp.able.exception.ServiceException;
import com.gitee.qdbp.base.annotation.OperateRecord;
import com.gitee.qdbp.base.system.model.IAccount;
import com.gitee.qdbp.base.system.service.IDefaultValueConfig;
import com.gitee.qdbp.tools.files.PathTools;
import com.gitee.qdbp.tools.utils.DateTools;
import com.gitee.qdbp.tools.utils.VerifyTools;

/**
 * 操作控制切面
 *
 * @author zhaohuihua
 * @version 160426
 */
public abstract class OperateRecordBaseAspect extends OperateRecordBaseControlling {

    protected static final Logger log = LoggerFactory.getLogger(OperateRecordBaseAspect.class);
    /** 最近一次查询操作描述发生异常的时间 **/
    private long lastTimeOfQueryOperateDescError = 0;
    /** 默认值配置表 **/
    private IDefaultValueConfig defaults;

    public OperateRecordBaseAspect(IControllingVars vars) {
        super(vars);
    }

    public Object doAround(ProceedingJoinPoint point) throws Throwable {
        this.start();
        Method method = getMethod(point);
        try {
            String permission = getControllerMethodPermission(method);
            Object returns = point.proceed();
            save(point, method, permission, returns);
            return returns;
        } catch (Throwable e) {
            save(point, method, null, e);
            throw e;
        } finally {
            this.clear();
        }
    }

    /** 获取租户编号, 如果user信息为null即表示用户未登录 **/
    protected String getTenantCode(IAccount user) {
        if (user != null) {
            return user.getTenantCode();
        } else if (defaults == null) {
            return null;
        } else {
            return defaults.getTenantCode();
        }
    }

    protected Method getMethod(ProceedingJoinPoint point) {

        if (!(point.getSignature() instanceof MethodSignature)) {
            return null;
        }
        MethodSignature signature = (MethodSignature) point.getSignature();
        Class<?> targetClass = point.getTarget().getClass();
        String methodName = signature.getName();
        Class<?>[] paramTypes = signature.getParameterTypes();
        Method method;
        try {
            method = targetClass.getMethod(methodName, paramTypes);
        } catch (NoSuchMethodException | SecurityException e) {
            return null;
        }
        return method;
    }

    protected MultipartFile findUploadFile(ProceedingJoinPoint point) {
        CopyUploadFileMode copyUploadFileMode = getCopyUploadFileMode();
        if (copyUploadFileMode == null || copyUploadFileMode == CopyUploadFileMode.NONE) {
            return null;
        }
        Object[] args = point.getArgs();
        List<String> copyUploadFileExtension = getCopyUploadFileExtension();
        for (Object i : args) {
            if (i instanceof MultipartFile) {
                MultipartFile temp = (MultipartFile) i;
                String extension = PathTools.getExtension(temp.getOriginalFilename());
                if (VerifyTools.isBlank(copyUploadFileExtension)) {
                    return temp; // 任何文件都复制
                }
                if (VerifyTools.isNotBlank(extension) && copyUploadFileExtension.indexOf(extension) >= 0) {
                    return temp; // 只复制特定文件
                }
            }
        }
        return null;
    }

    protected void save(ProceedingJoinPoint point, Method method, String permission, Object returns) {

        // 获取操作描述
        String sign = point.getSignature().toShortString();

        OperateRecord annotation = findOperateRecordAnnotation(method);
        if (annotation != null && !annotation.enable()) {
            return;
        }

        String operateDesc;
        if (annotation != null && VerifyTools.isNotBlank(annotation.value())) {
            operateDesc = annotation.value();
        } else {
            operateDesc = getAndCheckOperateDesc(permission, sign);
        }

        Class<?> clazz = point.getTarget().getClass();
        MultipartFile file = findUploadFile(point);
        save(clazz, method, sign, operateDesc, permission, annotation, returns, null, file, null);
    }

    /**
     * 获取Controller方法上配置的@RequiresPermissions的权限内容<br>
     * 如配置@RequiresPermissions("personnel:admin:create"), 则返回personnel:admin:create
     * 
     * @return 权限KEY
     */
    private String getControllerMethodPermission(Method method) {
        RequiresPermissions permission = method.getAnnotation(RequiresPermissions.class);
        if (permission == null || permission.value() == null) {
            return null;
        } else {
            return permission.value()[0];
        }
    }

    /**
     * 根据权限KEY从数据库中查询操作名称<br>
     * 每个项目的处理方式可能不一样, 留给子类实现
     * 
     * @param key
     * @return
     */
    protected String queryOperateDescByPermission(String key) throws ServiceException {
        return null;
    }

    protected OperateRecord findOperateRecordAnnotation(Method method) {
        OperateRecord annotation = AnnotationUtils.findAnnotation(method, OperateRecord.class);
        if (annotation == null) {
            annotation = AnnotationUtils.findAnnotation(method.getDeclaringClass(), OperateRecord.class);
        }
        return annotation;
    }

    protected String getAndCheckOperateDesc(String permission, String sign) {

        if (permission == null) {
            log.trace("@RequiresPermissions not found, at {}.", sign);
            return null;
        } else {
            String operate = null;
            try {
                operate = queryOperateDescByPermission(permission);
                if (operate == null) {
                    log.trace("Permission resource {} not found, at {}.", permission, sign);
                }
            } catch (ServiceException e) {
                long mills = System.currentTimeMillis();
                if (mills - lastTimeOfQueryOperateDescError > DateTools.RATE_HOUR) {
                    lastTimeOfQueryOperateDescError = mills;
                    log.warn("Query operate desc by {} error, at {}. {}.", permission, sign, e.toString(), e);
                }
            }
            return operate;
        }
    }

    /** 默认值配置表 **/
    public void setDefaultValueConfig(IDefaultValueConfig defaults) {
        this.defaults = defaults;
    }

    /** 默认值配置表 **/
    public IDefaultValueConfig getDefaultValueConfig() {
        return defaults;
    }

}
